enum Condition {
  EQ,
  NEQ,
  GT,
  LT,
  GE,
  LE,
  IN,
  LIKE,
  NOT_IN,
  NOT_LIKE,
  IS_NULL,
  IS_NOT_NULL,
  AND,
  OR,
  LEFT_JOIN,
  RIGHT_JOIN
}

class QueryConfig<T> {
  final String? field;
  final Condition condition;
  final T? value;
  final List<QueryConfig> children;

  QueryConfig({required this.field, required this.condition, required this.value, this.children = const []});

}

class QueryConfigWhere {
  final StringBuffer _where = StringBuffer();
  List args = [];

  String get where => _where.toString();

  QueryConfigWhere.of(List<QueryConfig> configs) {
    assert(configs.isNotEmpty);
    _buildCondition(configs, Condition.AND);
  }

  QueryConfigWhere and(List<QueryConfig> configs) {
    _buildCondition(configs, Condition.AND);
    return this;
  }

  QueryConfigWhere or(List<QueryConfig> configs) {
    _buildCondition(configs, Condition.OR);
    return this;
  }

  void _buildCondition(List<QueryConfig> configs, Condition condition) {
    if (_where.toString().endsWith(")")) _where.write(_getConditionStr(Condition.AND));
    _where.write("(");
    for (int i = 0; i < configs.length; ++i) {
      var qc = configs[i];
      var expr = _getConditionExpr(qc.condition), val = _getConditionVal(qc.value, qc.condition);
      if (expr != null) _where.write(" ${qc.field}${_getConditionExpr(qc.condition)} ${i != configs.length - 1 ? _getConditionStr(condition) : ""}");
      if (val != null) args.add(val);
    }
    _where.write(")");
  }

  String _getConditionStr(Condition condition) {
    switch(condition) {
      case Condition.AND:
        return " AND ";
      case Condition.OR:
        return " OR ";
      default:
        return "";
    }
  }

  String? _getConditionExpr(Condition condition) {
    switch(condition) {
      case Condition.EQ:
        return " = ?";
      case Condition.NEQ:
        return " != ?";
      case Condition.GT:
        return " > ?";
      case Condition.LT:
        return " < ?";
      case Condition.GE:
        return " >= ?";
      case Condition.LE:
        return " <= ?";
      case Condition.IN:
        return " IN ?";
      case Condition.LIKE:
        return " LIKE ?";
      case Condition.NOT_IN:
        return " NOT IN ?";
      case Condition.IS_NULL:
        return " IS NULL";
      case Condition.IS_NOT_NULL:
        return " IS NOT NULL";
      default:
        return null;
    }
  }

  String? _getConditionVal(dynamic value, Condition condition) {
    switch(condition) {
      case Condition.EQ:
      case Condition.NEQ:
      case Condition.GT:
      case Condition.LT:
      case Condition.GE:
      case Condition.LE:
        return value;
      case Condition.IN:
      case Condition.NOT_IN:
        return "(${(value as List).join(",")})";
      case Condition.LIKE:
      case Condition.NOT_LIKE:
        return "%$value%";
      case Condition.IS_NULL:
      case Condition.IS_NOT_NULL:
        return null;
      default:
        return null;
    }
  }
}
